package base.thread_demo.demo11;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 线程通信案例:
 * 生产者（Producer）将产品交给店员（Clerk），而消费者（Consumer）从店员处取走产品，
 * 店员一次性只能持有固定数量的产品（比如：20），如果生产者试图生产更多的产品，店员会叫生产者停一下，
 * 如果店中有空位放产品了再通知生产者继续生产；如果店中没有产品了，店员会告诉
 * 消费者等一下，如果店中有产品了再通知消费者来取走产品。
 *
 */
public class CommunicationDemo3 {

    public static void main(String[] args) {
        Clerk clerk = new Clerk(20);//定义20个长度的队列容器

        Producer p = new Producer(clerk);
        Consumer c = new Consumer(clerk);

        new Thread(c, "消费者-1").start();
        new Thread(p, "生产者-1").start();
        new Thread(c, "消费者-2").start();
    }
}

class Producer implements Runnable{
    private static final Logger log = LoggerFactory.getLogger(Producer.class);
    private Clerk clerk;
    public Producer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        log.info("{}开始生产商品......", Thread.currentThread().getName());
        while (true){
            try {
                clerk.produceProduct();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


class Consumer implements Runnable{
    private static final Logger log = LoggerFactory.getLogger(Consumer.class);
    private Clerk clerk;
    public Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        log.info("{}开始消费商品......", Thread.currentThread().getName());
        while (true){
            try {
                clerk.consumeProduct();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/*
 * 将生产商品(produceProduct)和消费商品(consumeProduct)添加为同步方法且持有同一把同步监视器锁，这样这两个方法就能实现
 * 并发时的排他
 */
class Clerk {
    private static final Logger log = LoggerFactory.getLogger(Clerk.class);
    private int queue;// 接收指定队列大小
    private int count=0;// 已生产总数
    public Clerk(int queue){
        this.queue = queue;
    }

    //生产商品
    public synchronized void produceProduct() throws InterruptedException {
        while (count >= queue){
            this.wait();
        }
        this.count++;
        log.info("{} 生产商品->{}",Thread.currentThread().getName(),this.count);
        this.notifyAll();
    }

    //消费商品
    public synchronized void consumeProduct() throws InterruptedException {
        while (count <= 0){//无存货,消费者进入等待
            this.wait();
        }
        log.info("-------------------->{} 消费商品->{}",Thread.currentThread().getName(),this.count);
        this.count--;
        this.notifyAll();
    }
}
